#include <tuple>
#include "lib/easysql.h"
#include "config.hh"

namespace atm_server
{

class database
{
public:
    static database& instance()
    {
        static database inst;
        return inst;
    }

    std::pair<double, bool> balance(const std::string& card_number, const std::string& password)
    {
        easymysql::result res = easymysql::executer(_conn)(R"(
SELECT balance FROM accounts WHERE card_number = '{}' AND password = '{}'
            )", card_number, password);

        easylog::debug("SELECT balance FROM accounts WHERE card_number = '{}' AND password = '{}'", card_number, password);

        std::tuple<double> line;
        if (res.get<std::tuple<double>>(&line))
        {
            easylog::debug("balance: {}", std::get<0>(line));
            return {std::get<0>(line), true};
        }
        return {-1, false};
    }

    bool deposit(const std::string& card_number, const std::string& password, double amount)
    {
        easylog::debug("UPDATE accounts SET balance = balance + {} WHERE card_number = '{}' AND password = '{}'", amount, card_number, password);

        try
        {
            easymysql::executer(_conn).execute(R"(
UPDATE accounts SET balance = balance + {} WHERE card_number = '{}' AND password = '{}';
            )", amount, card_number, password);
        }
        catch(const std::exception& e)
        {
            easylog::error("deposit failed exception : {}", e.what());
            return false;
        }

        return true;
    }

    bool withdraw(const std::string& card_number, const std::string& password, double amount)
    {
        easylog::debug("\n\tUPDATE accounts SET balance = balance - {} WHERE card_number = '{}' AND password = '{}'"
            , amount, card_number, password);

        try
        {
            easymysql::executer(_conn).execute(R"(
UPDATE accounts SET balance = balance - {} WHERE card_number = '{}' AND password = '{}';
            )", amount, card_number, password);
        }
        catch(const std::exception& e)
        {
            easylog::error("withdraw failed exception : {}", e.what());
            return false;
        }

        return true;
    }

    bool transfer(const std::string& card_number, const std::string& password, const std::string& to_card_number, double amount)
    {
        easylog::debug("\n\tUPDATE accounts SET balance = balance - {} WHERE card_number = '{}' AND password = '{}'"
                       "\n\tUPDATE accounts SET balance = balance + {} WHERE card_number = '{}'", amount, card_number, password, amount, to_card_number);

        try
        {
            easymysql::executer(_conn).execute(R"(START TRANSACTION;)");
            easymysql::executer(_conn).execute(R"(UPDATE accounts SET balance = balance - {} WHERE card_number = '{}' AND password = '{}';)", amount, card_number, password);
            easymysql::executer(_conn).execute(R"(UPDATE accounts SET balance = balance + {} WHERE card_number = '{}';)", amount, to_card_number);
            easymysql::executer(_conn).execute(R"(COMMIT;)");
        }
        catch(const std::exception& e)
        {
            easylog::error("transfer failed exception : {}", e.what());
            return false;
        }

        return true;
    }

private:
    database()
        : _conn(config::instance().sql_host(),
                config::instance().sql_user(),
                config::instance().sql_passwd(),
                config::instance().sql_dbname(),
                config::instance().sql_port())
    {}
    database(const database&) = delete;
    database& operator=(const database&) = delete;

private:
    easymysql::connection _conn;
};

}
